package sk.hackcraft.util.agent_simulation.agent;

import sk.hackcraft.util.agent_simulation.AgentSimulation;
import sk.hackcraft.util.agent_simulation.AgentSimulationComponent;
import sk.hackcraft.util.agent_simulation.messaging.Message;
import sk.hackcraft.util.agent_simulation.messaging.MessageCode;
import sk.hackcraft.util.agent_simulation.messaging.MessageProcessor;
import sk.hackcraft.util.agent_simulation.messaging.MessageType;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

public abstract class Manager extends AgentSimulationComponent implements MessageProcessor
{
	private final Agent agent;
	private Set<MessageCode> ownMessages;
	private Set<MessageCode> subordinateAgentsMessages;
	
	public Manager(AgentSimulation agentSimulation, Agent agent)
	{
		super(agentSimulation);
		ownMessages = new TreeSet<MessageCode>();
		subordinateAgentsMessages = new TreeSet<MessageCode>();
		
		this.agent = agent;
	}
	
	protected Agent getAgent()
	{
		return agent;
	}
	
	public void addOwnMessage(MessageCode messageCode)
	{
		ownMessages.add(messageCode);
		
	}
	
	public void request(Message message)
	{
		message.setMessageType(MessageType.REQUEST);
		message.addToSendersBuffer(this);
		message.setDeliveryTime(getAgentSimulation().getCurrentTime());
		getAgentSimulation().sendMessage(message);
	}
	
	public void response(Message message)
	{
		if(message.canBeReturned())
		{
			message.setMessageType(MessageType.RESPONSE);
			message.setDeliveryTime(getAgentSimulation().getCurrentTime());
			message.setLastSenderAsAddressee();
			getAgentSimulation().sendMessage(message);
		}
	}
	
	public void notice(Message message)
	{
		message.setMessageType(MessageType.NOTICE);
		message.setDeliveryTime(getAgentSimulation().getCurrentTime());
		getAgentSimulation().sendMessage(message);
	}
	
	public boolean start(Message message)
	{
		if(agent.hasContinualAssistant())
		{
			message.setMessageType(MessageType.START);
			message.setDeliveryTime(getAgentSimulation().getCurrentTime());
			message.setAddressee(agent.getContinualAssistant());
			getAgentSimulation().sendMessage(message);
			
			return true;
		}
		else
		{
			return false;
		}
	}
	
	@Override
	public void processMessage(Message message)
	{
		if(ownMessages.contains(message.getMessageCode()))
		{
			processMessageAction(message);
		}
		
		if(message.hasAddressee() && subordinateAgentsMessages.contains(message.getMessageCode()))
		{
			if(agent.getSubordinateAgents().size() > 1)
			{
				forwardMessageToSubordinateAgents(message);
			}
			else
			{
				forwardMessageToSingleSubordinateAgent(message);
			}
		}
	}
	
	protected abstract void processMessageAction(Message message);
	
	private void forwardMessageToSingleSubordinateAgent(Message message)
	{
		Iterator<Agent> iterator = agent.getSubordinateAgents().iterator();
		
		if(iterator.hasNext())
		{
			Agent subAgent = iterator.next();
			message.setAddressee(subAgent.getManager());

			forwardMessage(message);
		}
	}
	
	private void forwardMessageToSubordinateAgents(Message message)
	{
		for(Agent subAgent : agent.getSubordinateAgents())
		{
			Message newMessage = message.createCopy();
			newMessage.setAddressee(subAgent.getManager());

			forwardMessage(newMessage);
		}
	}
	
	private void forwardMessage(Message message)
	{
		switch(message.getMessageType())
		{
			case REQUEST:
				request(message);
				break;

			case RESPONSE:
				response(message);
				break;

			case NOTICE:
				notice(message);
				break;
		}
	}
	
	protected Set<MessageCode> getOwnMessages()
	{
		return this.ownMessages;
	}
	
	public void updateMessageCodeStructure()
	{
		for(Agent subAgent : agent.getSubordinateAgents())
		{
			Manager manager = subAgent.getManager();
			manager.updateMessageCodeStructure();
			
			subordinateAgentsMessages.addAll(manager.ownMessages);
			subordinateAgentsMessages.addAll(manager.subordinateAgentsMessages);
		}
	}
}
